#include "LoggerManager.h"
#include "LogLayout.h"
#include "json11/json11.hpp"
#include <iostream>
#include <map>
#include <vector>
#include <functional>

namespace log
{

LoggerManager::LoggerManager()
{

}

LoggerManager::~LoggerManager()
{

}

void LoggerManager::init(const std::string& strFile)
{
    std::ifstream ifs(strFile);
    if (!(ifs.is_open()))
    {
        return;
    }

    std::string strJson((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); 

    std::string err;
    json11::Json json =  json11::Json::parse(strJson, err, json11::STANDARD);
    if (!err.empty())
    {
        std::cout << err << std::endl;
        return;
    }
    static std::map<char, std::function<LogLayout::LogLayoutItem::ptr(const std::string& str)> > mapFuncs{
        {'Y', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<YearLayoutItem>(str); }},
        {'m', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<MouthLayoutItem>(str); }},
        {'d', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<DayLayoutItem>(str); }},
        {'H', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<HourLayoutItem>(str); }},
        {'M', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<MinLayoutItem>(str); }},
        {'S', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<SecondLayoutItem>(str); }},
        {'u', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<MSecLayoutItem>(str); }},
        {'L', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<LevelLayoutItem>(str); }},
        {'T', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<ThreadIdLayoutItem>(str); }},
        {'f', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<FileLayoutItem>(str); }},
        {'l', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<LineLayoutItem>(str); }},
        {'F', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<FuncLayoutItem>(str); }},
        {'s', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<StringLayoutItem>(str); }},
        {'i', [](const std::string& str) ->LogLayout::LogLayoutItem::ptr  { return std::make_shared<MsgLayoutItem>(str); }},
    };

#define XX(ch, str, name, m) \
    switch (ch) \
    { \
    case 'Y': case 'm': case 'd': \
    case 'H': case 'M': case 'S': \
    case 'L': case 'T': case 'f': \
    case 'l': case 'F': \
        m[name]->addLogLayoutItem((mapFuncs[ch])(str)); break; \
    case '%': break; \
    default: \
        m[name]->addLogLayoutItem((mapFuncs['s'])(str)); break; \
    }

    const json11::Json::object layouts = json["layout"].object_items();
    std::map<std::string, LogLayout::ptr > mapLayouts;
    for (auto itr : layouts)
    {
        std::string name = itr.first;
        std::string strlayout = itr.second.string_value();
        mapLayouts[name] = std::make_shared<LogLayout>();
        int start = -1;
        for (int i = 0; i < strlayout.size(); i++)
        {
            if (start == -1)
            {
                start = i;
            }
            if (strlayout[i] == '%')
            {
                if (start != i)
                {
                     XX('s', strlayout.substr(start, i-start), name, mapLayouts);
                     start = i;
                }

                while (true)
                {
                    ++i;
                    if (i > strlayout.size())
                    {
                        break;
                    }
                    auto itr = mapFuncs.find(strlayout[i]);
                    if (itr != mapFuncs.end())
                    {
                        mapLayouts[name]->addLogLayoutItem(((itr->second)(strlayout.substr(start, i-start))));
                        start = -1;
                        break;
                    }
                }
            }
        }

#undef XX 
    }
    
    const json11::Json::object apps = json["appender"].object_items();
    std::map<std::string, LogAppender::ptr> mapApps;
    for (auto itr : apps)
    {
        std::string name = itr.first;
        json11::Json jsonapp = itr.second;
        if (jsonapp["type"].string_value().compare("file") == 0)
        {
            FileAppender::ptr fileApp = std::make_shared<FileAppender>(jsonapp["file_name"].string_value());
            fileApp->setFileMaxSize(jsonapp["max_size"].string_value());
            fileApp->setLayout(mapLayouts[jsonapp["layout"].string_value()]);
            mapApps[name] = fileApp;
        }
        else if (jsonapp["type"].string_value().compare("stdout") == 0)
        {
            StdoutAppender::ptr stdoutApp = std::make_shared<StdoutAppender>();
            stdoutApp->setLayout(mapLayouts[jsonapp["layout"].string_value()]);
            mapApps[name] = stdoutApp;
        }
    }

    const json11::Json::object loggers = json["logger"].object_items();
    for (auto itr : loggers)
    {
        std::string name = itr.first;
        Logger::ptr ptrLogger = std::make_shared<Logger>(name);
        const json11::Json logger = itr.second;
        ptrLogger->setLevel(LogLevel::fromString(logger["level"].string_value()));
        const json11::Json apps = logger["appender"];
        for (auto app: apps.array_items())
        {
            auto iter = mapApps.find(app.string_value());
            if (iter != mapApps.end())
            {
                ptrLogger->addAppender(iter->second);
            }
        }
        updateLogger(name, ptrLogger);
    }
}
    
Logger::ptr LoggerManager::getLogger(const std::string& name)
{
    {
        utils::ReadLock lock(m_mutex);
        auto itr = m_mapLoggers.find(name);
        if (itr != m_mapLoggers.end() && itr->second)
        {
            return itr->second;
        }
    }

    Logger::ptr ptrLogger = std::make_shared<Logger>(name);
    ptrLogger->setLevel(LogLevel::ELevel::ERROR);
    ptrLogger->addAppender(std::make_shared<log::StdoutAppender>());
    updateLogger(name, ptrLogger);
    return ptrLogger;
}

void LoggerManager::updateLogger(const std::string& name, Logger::ptr ptrLogger)
{
    if (ptrLogger == nullptr)
    {
        return;
    }

    utils::WriteLock lock(m_mutex);
    auto itr = m_mapLoggers.find(name);
    if (itr != m_mapLoggers.end())
    {
        itr->second->setLevel(ptrLogger->getLevel());
        itr->second->setAppender(ptrLogger->getAppender());
    }
    else
    {
        m_mapLoggers[name] = ptrLogger; 
    }
}

void LoggerManager::deleteLogger(const std::string& name)
{
    utils::WriteLock lock(m_mutex);
    m_mapLoggers.erase(name);
}

} // namespace log
